import 'dotenv/config';
import express from "express";
import cors from "cors";
import { v4 as uuidv4 } from "uuid";
import { query } from "./db/mysql.js";
import { signToken, authRequired } from "./middleware/auth.js";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import http from "http";
import https from "https";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT = path.resolve(__dirname, "..");

// 可配置的外链代理请求头（用于绕过 403）
const {
  PROXY_REFERER,
  PROXY_ORIGIN,
  PROXY_COOKIE,
  PROXY_USER_AGENT
} = process.env;

const app = express();
app.use(cors());
app.use(express.json({ limit: "2mb" }));
// 静态资源：本地音频目录 -> /media/*
app.use("/media", express.static(path.join(ROOT, "public", "audio")));

// In-memory captcha store: uuid -> { code, expireAt }
const captchaStore = new Map();
const CAPTCHA_TTL_MS = 2 * 60 * 1000;

const PORT = process.env.PORT || 3000;

// Utils
function sendOk(res, data, msg = "操作成功", code = 200) {
  return res.json({ code, msg, ...(data !== undefined ? { data } : {}) });
}

// 1.1 GET /captchaImage
app.get("/captchaImage", (req, res) => {
  const uuid = uuidv4();
  const code = Math.floor(1000 + Math.random() * 9000).toString(); // 4-digit
  const expireAt = Date.now() + CAPTCHA_TTL_MS;
  captchaStore.set(uuid, { code, expireAt });

  const svg = `
  <svg xmlns='http://www.w3.org/2000/svg' width='100' height='40'>
    <rect width='100' height='40' fill='#f2f2f2'/>
    <text x='50%' y='50%' dominant-baseline='middle' text-anchor='middle'
          font-family='Arial' font-size='20' fill='#333'>${code}</text>
  </svg>`;
  const base64 = Buffer.from(svg).toString("base64");
  const img = `data:image/svg+xml;base64,${base64}`;

  return res.json({ code: 200, msg: "操作成功", uuid, img });
});

// 1.2 POST /login
app.post("/login", async (req, res) => {
  const { username, password, code, uuid } = req.body || {};
  if (!username || !password || !code || !uuid) {
    return res.status(400).json({ code: 400, msg: "参数不完整" });
  }
  const record = captchaStore.get(uuid);
  captchaStore.delete(uuid); // 单次使用
  if (!record || record.expireAt < Date.now() || record.code !== code) {
    return res.status(400).json({ code: 400, msg: "验证码无效或已过期" });
  }

  const rows = await query("SELECT id, username, password FROM users WHERE username = ?", [username]);
  const user = rows[0];
  if (!user || user.password !== password) {
    return res.status(401).json({ code: 401, msg: "用户名或密码错误" });
  }

  const token = signToken({ userId: user.id, username: user.username }, "7d");
  const avatar = `https://api.dicebear.com/7.x/identicon/svg?seed=${encodeURIComponent(user.username)}`;
  return res.json({
    code: 200,
    msg: "登录成功",
    token,
    data: {
      user: {
        id: user.id,
        username: user.username
      }
    }
  });
});

// 2.1 GET /home/content (auth)
app.get("/home/content", authRequired, async (req, res) => {
  const songs = await query(
    "SELECT id, title, artist, album, cover, duration FROM songs ORDER BY id LIMIT 6"
  );
  const hotSongs = songs.slice(0, 3);
  const recommendedSongs = songs.slice(1, 6);
  return sendOk(res, { hotSongs, recommendedSongs }, "查询成功");
});

// 3.1 GET /song/detail/:songId (auth)
app.get("/song/detail/:songId", authRequired, async (req, res) => {
  const id = Number(req.params.songId);
  if (!id) return res.status(400).json({ code: 400, msg: "参数不完整" });
  const rows = await query(
    "SELECT id, title, artist, album, cover, duration, url, lyrics FROM songs WHERE id = ?",
    [id]
  );
  const song = rows[0];
  if (!song) return res.status(404).json({ code: 404, msg: "歌曲不存在" });
  return sendOk(res, song, "查询成功");
});

// 4.1 POST /user/favorites/toggle (auth)
app.post("/user/favorites/toggle", authRequired, async (req, res) => {
  const userId = Number(req.user.userId);
  const idNum = Number(req.body?.songId);
  if (!idNum) return res.status(400).json({ code: 400, msg: "参数不完整" });

  const song = (await query("SELECT id FROM songs WHERE id = ?", [idNum]))[0];
  if (!song) return res.status(404).json({ code: 404, msg: "歌曲不存在" });

  const existing = (await query(
    "SELECT 1 FROM favorites WHERE user_id = ? AND song_id = ?",
    [userId, idNum]
  ))[0];

  if (existing) {
    await query("DELETE FROM favorites WHERE user_id = ? AND song_id = ?", [userId, idNum]);
    return res.json({ code: 200, msg: "已取消收藏", data: { isFavorited: false } });
  } else {
    await query("INSERT INTO favorites (user_id, song_id) VALUES (?, ?)", [userId, idNum]);
    return res.json({ code: 200, msg: "收藏成功", data: { isFavorited: true } });
  }
});

// 4.2 GET /user/favorites (auth)
app.get("/user/favorites", authRequired, async (req, res) => {
  const userId = Number(req.user.userId);
  const rows = await query(
    `SELECT s.id, s.title, s.artist, s.cover, s.duration
     FROM songs s
     INNER JOIN favorites f ON f.song_id = s.id
     WHERE f.user_id = ?
     ORDER BY s.id DESC`,
    [userId]
  );
  return sendOk(res, rows, "查询成功");
});

// 5.1 POST /user/history/log (auth)
app.post("/user/history/log", authRequired, async (req, res) => {
  const userId = Number(req.user.userId);
  const idNum = Number(req.body?.songId);
  if (!idNum) return res.status(400).json({ code: 400, msg: "参数不完整" });

  const song = (await query("SELECT id FROM songs WHERE id = ?", [idNum]))[0];
  if (!song) return res.status(404).json({ code: 404, msg: "歌曲不存在" });

  await query("INSERT INTO history (user_id, song_id) VALUES (?, ?)", [userId, idNum]);
  return res.json({ code: 200, msg: "记录成功" });
});

// 5.2 GET /user/history (auth)
app.get("/user/history", authRequired, async (req, res) => {
  const userId = Number(req.user.userId);
  const rows = await query(
    `SELECT s.id, s.title, s.artist, s.cover, s.duration, h.played_at AS playedAt
     FROM history h
     INNER JOIN songs s ON s.id = h.song_id
     WHERE h.user_id = ?
     ORDER BY h.played_at DESC
     LIMIT 500`,
    [userId]
  );
  return sendOk(res, rows, "查询成功");
});

// 5.3 DELETE /user/history/clear (auth)
app.delete("/user/history/clear", authRequired, async (req, res) => {
  const userId = Number(req.user.userId);
  await query("DELETE FROM history WHERE user_id = ?", [userId]);
  return res.json({ code: 200, msg: "播放历史已清空" });
});

 // 音频流式播放：GET /song/stream/:songId
// 优先本地文件（songs.url 以 /media/ 开头，对应 public/audio/*）并处理 Range 请求
// 若 url 为 http(s) 外链，临时 302 重定向
app.get("/song/stream/:songId", async (req, res) => {
  const id = Number(req.params.songId);
  if (!id) return res.status(400).json({ code: 400, msg: "参数不完整" });
  const rows = await query("SELECT url, title FROM songs WHERE id = ?", [id]);
  const song = rows[0];
  if (!song || !song.url) return res.status(404).json({ code: 404, msg: "资源不存在" });

  const src = String(song.url);
  if (src.startsWith("/media/")) {
    const rel = src.replace(/^\/media\//, "");
    const filePath = path.join(ROOT, "public", "audio", rel);
    if (!fs.existsSync(filePath)) {
      return res.status(404).json({ code: 404, msg: "文件未找到" });
    }
    const stat = fs.statSync(filePath);
    const fileSize = stat.size;
    const range = req.headers.range;

    res.setHeader("Accept-Ranges", "bytes");
    res.setHeader("Content-Type", "audio/mpeg");

    if (range) {
      const parts = range.replace(/bytes=/, "").split("-");
      const start = parseInt(parts[0], 10);
      const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
      if (isNaN(start) || isNaN(end) || start > end || end >= fileSize) {
        return res.status(416).set({
          "Content-Range": `bytes */${fileSize}`
        }).end();
      }
      const chunkSize = end - start + 1;
      res.writeHead(206, {
        "Content-Range": `bytes ${start}-${end}/${fileSize}`,
        "Content-Length": chunkSize
      });
      const stream = fs.createReadStream(filePath, { start, end });
      return stream.pipe(res);
    } else {
      res.setHeader("Content-Length", fileSize);
      const stream = fs.createReadStream(filePath);
      return stream.pipe(res);
    }
  }

  // 代理外链：支持 Range，透传内容长度与类型
  try {
    const client = src.startsWith("https") ? https : http;
    const headers = {
      "User-Agent": PROXY_USER_AGENT || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124 Safari/537.36",
      "Accept": "*/*"
    };
    if (PROXY_REFERER) headers.Referer = PROXY_REFERER;
    if (PROXY_ORIGIN) headers.Origin = PROXY_ORIGIN;
    if (PROXY_COOKIE) headers.Cookie = PROXY_COOKIE;
    if (req.headers.range) headers.Range = req.headers.range;

    const upstream = client.request(src, { method: "GET", headers }, (upRes) => {
      // 处理 200/206 正常流
      if ([200, 206].includes(upRes.statusCode || 0)) {
        // 透传关键响应头
        const passHeaders = ["content-type", "content-length", "accept-ranges", "content-range"];
        for (const h of passHeaders) {
          const v = upRes.headers[h];
          if (v !== undefined) res.setHeader(h, v);
        }
        if (!res.getHeader("Content-Type")) res.setHeader("Content-Type", "audio/mpeg");
        res.status(upRes.statusCode || 200);
        upRes.pipe(res);
      } else if (upRes.statusCode && upRes.statusCode >= 300 && upRes.statusCode < 400 && upRes.headers.location) {
        // 上游再重定向，跟随一次
        const redirected = upRes.headers.location;
        const client2 = redirected.startsWith("https") ? https : http;
        const upstream2 = client2.request(redirected, { method: "GET", headers }, (upRes2) => {
          if ([200, 206].includes(upRes2.statusCode || 0)) {
            const passHeaders2 = ["content-type", "content-length", "accept-ranges", "content-range"];
            for (const h of passHeaders2) {
              const v = upRes2.headers[h];
              if (v !== undefined) res.setHeader(h, v);
            }
            if (!res.getHeader("Content-Type")) res.setHeader("Content-Type", "audio/mpeg");
            res.status(upRes2.statusCode || 200);
            upRes2.pipe(res);
          } else {
            res.status(502).json({ code: 502, msg: "上游音频不可用" });
          }
        });
        upstream2.on("error", () => res.status(502).json({ code: 502, msg: "上游连接失败" }));
        upstream2.end();
      } else if (upRes.statusCode === 403) {
        res.status(403).json({ code: 403, msg: "外链拒绝访问，请改为本地 /media 或更换源" });
      } else {
        res.status(502).json({ code: 502, msg: "上游音频不可用" });
      }
    });
    upstream.on("error", () => res.status(502).json({ code: 502, msg: "上游连接失败" }));
    upstream.end();
  } catch (e) {
    return res.status(500).json({ code: 500, msg: "代理转发失败" });
  }
});

/**
 * 6.1 GET /api/v1/search/songs (auth)
 * 查询参数：
 * - keyword（可选）：同时在 title 与 artist 上做模糊匹配
 * - title（可选）：按歌曲名称模糊匹配
 * - artist（可选）：按作者/歌手模糊匹配
 * - page（可选，默认1）
 * - pageSize（可选，默认20，最大100）
 * - orderBy（可选：relevance|hot|recent，默认relevance）
 * 返回：{ items, total, page, pageSize }
 */
const searchRateLimit = new Map(); // userId -> { windowStart:number, count:number }
const SEARCH_WINDOW_MS = 60 * 1000;
const SEARCH_LIMIT_PER_WINDOW = 60;

app.get("/api/v1/search/songs", authRequired, async (req, res) => {
  const userId = Number(req.user.userId) || 0;

  // 简易频率限制（每用户每分钟最多 60 次）
  const now = Date.now();
  const rl = searchRateLimit.get(userId);
  if (!rl || now - rl.windowStart >= SEARCH_WINDOW_MS) {
    searchRateLimit.set(userId, { windowStart: now, count: 1 });
  } else {
    rl.count += 1;
    if (rl.count > SEARCH_LIMIT_PER_WINDOW) {
      return res.status(429).json({ code: 429, msg: "频率过高，请稍后再试" });
    }
  }

  // 解析参数
  const q = req.query || {};
  const keyword = (q.keyword || "").trim();
  const title = (q.title || "").trim();
  const artist = (q.artist || "").trim();
  const page = Math.max(1, Number(q.page) || 1);
  let pageSize = Number(q.pageSize) || 20;
  pageSize = Math.max(1, Math.min(100, pageSize));
  const orderBy = ["relevance", "hot", "recent"].includes(String(q.orderBy)) ? String(q.orderBy) : "relevance";

  if (!keyword && !title && !artist) {
    return res.status(400).json({ code: 400, msg: "至少需要 keyword、title 或 artist 之一" });
  }

  // 构造 WHERE 与参数
  const whereParts = [];
  const params = [];

  if (keyword) {
    whereParts.push("(s.title LIKE ? OR s.artist LIKE ?)");
    params.push(`%${keyword}%`, `%${keyword}%`);
  }
  if (title) {
    whereParts.push("s.title LIKE ?");
    params.push(`%${title}%`);
  }
  if (artist) {
    whereParts.push("s.artist LIKE ?");
    params.push(`%${artist}%`);
  }
  const whereSql = whereParts.length ? "WHERE " + whereParts.join(" AND ") : "";

  // 关联播放次数
  const baseJoin = `
    FROM songs s
    LEFT JOIN (
      SELECT song_id, COUNT(*) AS playCount
      FROM history
      GROUP BY song_id
    ) h ON h.song_id = s.id
  `;

  // 相关性评分（简单规则）
  let orderSql = "";
  if (orderBy === "hot") {
    orderSql = "ORDER BY COALESCE(h.playCount, 0) DESC, s.id DESC";
  } else if (orderBy === "recent") {
    orderSql = "ORDER BY s.id DESC";
  } else {
    // relevance
    const kw = keyword || title || artist || ""; // 取一个代表词用于评分
    // 使用 CASE + LOCATE 实现粗略相关性：完全匹配 > 前缀匹配 > 包含
    orderSql = `
      ORDER BY
        (
          CASE
            WHEN ? <> '' AND (s.title = ? OR s.artist = ?) THEN 100
            WHEN ? <> '' AND (s.title LIKE CONCAT(?, '%') OR s.artist LIKE CONCAT(?, '%')) THEN 80
            WHEN ? <> '' AND (s.title LIKE CONCAT('%', ?, '%') OR s.artist LIKE CONCAT('%', ?, '%')) THEN 60
            ELSE 0
          END
        ) DESC,
        COALESCE(h.playCount, 0) DESC,
        s.id DESC
    `;
    // 填充评分参数
    params.push(kw, kw, kw, kw, kw, kw, kw, kw, kw);
  }

  // 统计总数
  const countSql = `SELECT COUNT(*) AS total ${baseJoin} ${whereSql}`;
  const [{ total }] = await query(countSql, params.slice(0, whereParts.length ? params.length - (orderBy === "relevance" ? 9 : 0) : params.length));

  // 分页
  const offset = (page - 1) * pageSize;

  // 查询列表
  const listSql = `
    SELECT
      s.id,
      s.title,
      s.artist,
      s.album,
      s.cover,
      s.duration,
      COALESCE(h.playCount, 0) AS playCount
    ${baseJoin}
    ${whereSql}
    ${orderSql}
    LIMIT ? OFFSET ?
  `;

  const listParams = [...params, pageSize, offset];
  const rows = await query(listSql, listParams);

  const items = rows.map(r => ({
    id: r.id,
    title: r.title,
    artist: r.artist,
    albumId: null,
    albumTitle: r.album || null,
    coverUrl: r.cover || null,
    durationMs: (Number(r.duration) || 0) * 1000,
    playCount: Number(r.playCount) || 0
  }));

  return sendOk(res, { items, total: Number(total) || 0, page, pageSize }, "查询成功");
});

app.listen(PORT, () => {
  console.log(`server running at http://localhost:${PORT}`);
});